package com.atguigu.cloud.utils;

import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * 动态SQL解析工具类
 * 支持MyBatis的动态SQL标签（如<if>、<where>等）和参数占位符（#{xxx}）
 * 使用MyBatis的XMLLanguageDriver解析动态SQL，然后用JdbcTemplate执行
 * 
 * @author atguigu
 * @since 2024-01-01
 */
@Component
public class DynamicSqlParser {

    private final Configuration configuration;
    private final XMLLanguageDriver languageDriver;

    public DynamicSqlParser() {
        this.configuration = new Configuration();
        this.languageDriver = new XMLLanguageDriver();
    }

    /**
     * 解析动态SQL，返回最终的SQL和参数列表
     * 
     * @param sqlTemplate SQL模板（可包含MyBatis动态标签和#{xxx}占位符）
     * @param params      参数Map
     * @return 包含解析后的SQL和参数的ParsedSql对象
     */
    public ParsedSql parseSql(String sqlTemplate, Map<String, Object> params) {
        try {
            // 包装SQL为完整的脚本格式
            String wrappedSql = wrapSqlScript(sqlTemplate);

            // 使用XMLLanguageDriver创建SqlSource
            SqlSource sqlSource = languageDriver.createSqlSource(configuration, wrappedSql, Map.class);

            // 获取BoundSql（包含最终SQL和参数）
            BoundSql boundSql = sqlSource.getBoundSql(params != null ? params : new HashMap<>());

            String sql = boundSql.getSql();
            Map<String, Object> paramMap = new HashMap<>();

            // 提取参数值（优先从additionalParameters获取，因为foreach等标签会生成额外参数）
            boundSql.getParameterMappings().forEach(mapping -> {
                String property = mapping.getProperty();
                // 优先从additionalParameters中获取（foreach等标签生成的参数在这里）
                if (boundSql.hasAdditionalParameter(property)) {
                    paramMap.put(property, boundSql.getAdditionalParameter(property));
                }
                // 其次从原始参数中获取
                else if (params != null && params.containsKey(property)) {
                    paramMap.put(property, params.get(property));
                }
                // 如果都没有，尝试从参数对象中获取（用于嵌套属性）
                else if (params != null) {
                    Object value = getNestedProperty(params, property);
                    if (value != null) {
                        paramMap.put(property, value);
                    }
                }
            });

            return new ParsedSql(sql, paramMap, boundSql.getParameterMappings(), boundSql);
        } catch (Exception e) {
            throw new RuntimeException("动态SQL解析失败: " + e.getMessage(), e);
        }
    }

    /**
     * 获取嵌套属性值
     */
    private Object getNestedProperty(Map<String, Object> params, String property) {
        if (property.contains(".")) {
            String[] parts = property.split("\\.");
            Object current = params.get(parts[0]);
            for (int i = 1; i < parts.length && current != null; i++) {
                if (current instanceof Map) {
                    current = ((Map<?, ?>) current).get(parts[i]);
                }
            }
            return current;
        }
        return null;
    }

    /**
     * 包装SQL为MyBatis脚本格式
     */
    private String wrapSqlScript(String sql) {
        sql = sql.trim();
        // 如果已经是<script>标签包裹的，直接返回
        if (sql.startsWith("<script>") && sql.endsWith("</script>")) {
            return sql;
        }
        // 否则包装一层<script>标签
        return "<script>" + sql + "</script>";
    }

    /**
     * 解析后的SQL对象
     */
    public static class ParsedSql {
        private final String sql;
        private final Map<String, Object> parameters;
        private final java.util.List<org.apache.ibatis.mapping.ParameterMapping> parameterMappings;
        private final BoundSql boundSql;

        public ParsedSql(String sql, Map<String, Object> parameters,
                java.util.List<org.apache.ibatis.mapping.ParameterMapping> parameterMappings,
                BoundSql boundSql) {
            this.sql = sql;
            this.parameters = parameters;
            this.parameterMappings = parameterMappings;
            this.boundSql = boundSql;
        }

        public String getSql() {
            return sql;
        }

        public Map<String, Object> getParameters() {
            return parameters;
        }

        public java.util.List<org.apache.ibatis.mapping.ParameterMapping> getParameterMappings() {
            return parameterMappings;
        }

        public BoundSql getBoundSql() {
            return boundSql;
        }

        /**
         * 获取JDBC可执行的SQL（将?占位符替换为实际值）
         */
        public String getExecutableSql() {
            String executableSql = sql;
            Object[] paramArray = new Object[parameterMappings.size()];

            for (int i = 0; i < parameterMappings.size(); i++) {
                String property = parameterMappings.get(i).getProperty();
                Object value = parameters.get(property);
                paramArray[i] = value;
            }

            return executableSql;
        }

        /**
         * 获取参数数组（按照SQL中?的顺序）
         * 特别处理foreach等标签产生的参数
         */
        public Object[] getParameterArray() {
            Object[] paramArray = new Object[parameterMappings.size()];
            for (int i = 0; i < parameterMappings.size(); i++) {
                String property = parameterMappings.get(i).getProperty();
                Object value = parameters.get(property);

                // 如果从parameters中没找到，尝试从BoundSql的additionalParameters中获取
                if (value == null && boundSql != null && boundSql.hasAdditionalParameter(property)) {
                    value = boundSql.getAdditionalParameter(property);
                }

                paramArray[i] = value;
            }
            return paramArray;
        }
    }
}
